import java.util.*;
import java.util.function.BiConsumer;

/*
map同conllection一样是一个集合体系，但是map是双列集合，一个元素包含两个值（key，value）

接口    实现类
       HashMap    LinkedHashMap
map    TreeMap
       Hashtable  Properties   (这两个涉及到IO流，后面会学习)
 */
public class map {
    public static void main(String[] args)  {
        /*
        map 双列集合，一个元素包含两个值（key，value）
        键+值这个整体叫 "键值对" 或 "键值对对象" 或 "entry对象"
        map的键不能重复，值可以重复


        常见方法：
        1. V put(K key, V value) 添加元素
        2. V remove(Object key) 删除元素
        3. void clear() 清空元素
        4. boolean containsKey(Object key) 判断是否包含某个键
        5. boolean containsValue(Object value) 判断是否包含某个值
        6. boolean isEmpty() 判断是否为空
        7. int size() 返回集合的长度
        8. V get(Object key) 根据键获取值
         */
         Map<String, Integer> map = new HashMap<>();
         map.put("aaa", 1);
         map.put("bbb", 2);
         map.put("ccc", 3);
         map.put("ccc", 4);
         //put()方法成功添加元素自己返回null，失败返回原来的值,同时会覆盖原来的值
         map.put("ddd", 5);

         System.out.println(map.remove("ddd"));//remove()方法返回被删除的值
        // map.clear();//clear()方法清空元素
        System.out.println(map.containsKey("aaa"));//containsKey()方法判断是否包含某个键
        System.out.println(map.containsValue(2));//containsValue()方法判断是否包含某个值
        System.out.println(map.isEmpty());//isEmpty()方法判断是否为空
        System.out.println(map.size());//size()方法返回集合的长度
        System.out.println(map.get("aaa"));//get()方法根据键获取值

        System.out.println(map);//{aaa=1, bbb=2, ccc=4}

        /*
        map集合的遍历方式：
        1.键找值
        2.键值对
        3.lambda表达式
         */
        System.out.println("\n---------------------------\nmap集合的遍历方式：");
        //1.键找值
        System.out.println("1.键找值遍历：");
        //获取所有键的集合
        Set<String> keys = map.keySet();//keySet()方法返回所有键的集合
        //遍历键的集合，获取到每一个键
        for (String key : keys) {
            //根据键获取值
            Integer value = map.get(key);
            System.out.println(key + "=" + value);
        }
        /*也可以这样写更直接简单，省去中间变量
        for (var key : map.keySet()) {
            System.out.println(key + "=" + map.get(key));
        }
        */

        //2.键值对
        System.out.println("\n2.键值对遍历：");
        //获取所有键值对对象的集合
        Set<Map.Entry<String, Integer>> entries = map.entrySet();//entrySet()方法返回所有键值对对象的集合
        //遍历键值对对象的集合，获取到每一个键值对对象
        for (Map.Entry<String, Integer> entry : entries) {
            //根据键值对对象获取键和值
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + "=" + value);
        }
       /*也可以这样写更直接简单，省去中间变量
        for (var entry : map.entrySet()) {
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
        */

        //3.lambda表达式
        System.out.println("\n3.lambda表达式遍历：");
        //lambda完整写法(写了这个后面两个简化版的写法不知道为什么会报错，可能是因为jdk版本的问题)
//       map.forEach(new BiConsumer<String, Integer>() {
//            @Override
//            public void accept(String key, Integer value) {
//                System.out.println(key + "=" + value);
//            }
//        });
//       }
        //lambda表达式第一次简化写法
        map.forEach((String key, Integer value)->{
            System.out.println(key + "=" + value);
        });
        //lambda表达式第二次简化写法
        map.forEach((key, value)->System.out.println(key + "=" + value));



        /*
        HashMap是Map接口的实现类，没有特有方法，直接使用Map接口中的方法即可
        特点都是由键值决定的：(存取)无序，不重复，无索引
        底层实现跟HashSet一样，都是哈希表结构
        当键值自定义对象时，需要重写hashCode()和equals()方法，值没这个要求
         */
        System.out.println("\n----------------------\nHashMap实现：");

        HashMap<Student1, String> hashMap = new HashMap<>();
        Student1 s1 = new Student1("李四", 13);
        Student1 s2 = new Student1("张三", 15);
        Student1 s3 = new Student1("王二", 11);
        hashMap.put(s1, "男");
        hashMap.put(s2, "女");
        hashMap.put(s3, "男");

        System.out.println(hashMap);
        //{Student1{name='李四', age=13}=男, Student1{name='张三', age=15}=女, Student1{name='王二', age=11}=男}
        //需要重写toString()方法不然打印的是地址值，如下
        //{Student1@1bb438f=男, Student1@16e8e07=女, Student1@18e5423=男}
        //当然也可以使用三种遍历方式跟map一样


        /*
        LinkedHashMap是HashMap的子类
        由键值决定：(存取)有序，不重复，无索引
        实现原理：哈希表+链表(记录元素的存储顺序)
        LinkedHashMap与HashMap关系与LinkedHashSet与HashSet关系一样，不多介绍
         */


        /*
        TreeMap跟TreeSet底层实现一样，都是红黑树结构
        由键值决定：排序，不重复，无索引
        可以对键进行排序，默认是升序排序

        两种自定义排序方式：
        1.实现Comparable接口，重写compareTo()方法
        2.创建集合时传递Comparator比较器对象(两种方式同时存在时，会按这个定义的排序方式排序)
         */
        //只示范第一种自定义排序方式
        System.out.println("\n----------------------\nTreeMap实现：");
        TreeMap<Student1, String> treeMap = new TreeMap<>();
        treeMap.put(s1, "男");
        treeMap.put(s2, "女");
        treeMap.put(s3, "男");
        System.out.println(treeMap);
    }
}


class Student1 implements Comparable<Student1> {
    private String name;
    private int age;

    public Student1(String name, int age) {
        this.name = name;
        this.age = age;
    }


    @Override
    public boolean equals(Object o) {
        if (o == null || getClass() != o.getClass()) return false;
        Student1 student1 = (Student1) o;
        return age == student1.age && Objects.equals(name, student1.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Student1{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student1 o) {
        //先比较年龄，年龄相同再比较名字
        int i = this.age - o.age;
        i = i == 0 ? this.name.compareTo(o.name) : i;
        return i;
    }

}